package com.tianji.learning.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.msg.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;

    @Override
    public SignResultVO addSignRecords() {
        // 1. 获取用户id
        Long userId = UserContext.getUser();
        // 2. 拼接key
        LocalDate now = LocalDate.now(); // 获取年月
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));// 这个可以得到 冒号年月
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId + format;
        // 3. 利用bitset命令  将记录保存到redis中的bitmap中
        int offset = now.getDayOfMonth() - 1;
        Boolean setBit = redisTemplate.opsForValue().setBit(key, offset, true);
        if (setBit){
            // 表示已经签过到了
            throw new BizIllegalException("不能重复签到");
        }
        // 4. 查询连续签到

        int days  = countSignDays(key,now.getDayOfMonth());
        // 5. 计算连续签到  奖励积分
        int rewardPoints = 0;
        switch (days){
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 40;
                break;
        }
//        if (days == 7){
//            rewardPoints = 10;
//        }else if (days == 14){
//            rewardPoints = 20;
//        }
//        if (days == 28){
//            rewardPoints = 40;
//        }
        // 6. 保存积分
        mqHelper.send(MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                SignInMessage.of(userId,rewardPoints + 1));
        // 7. 封装vo
        SignResultVO resultVO = new SignResultVO();
        resultVO.setSignDays(days);
        resultVO.setRewardPoints(rewardPoints);
        return resultVO;
    }

    /**
     * 计算本月连续签到
     * @param key
     * @param dayOfMonth
     * @return
     */
    private int countSignDays(String key, int dayOfMonth) {
        // 求本月第一天到今天的所有签到数据
        List<Long> bitField = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(bitField)){
            return 0;
        }
        Long num = bitField.get(0); // 这个拿出来的是一个十进制
        log.info("数据：{}",num);
        int counter = 0;
        // &会把num转化为二进制来跟一做比较
        while ((num & 1) == 1){
            counter++;
            num = num >>> 1;
        }
        return counter;
    }

    @Override
    public Byte[] querySignRecords() {
        // 1.获取用户id
        Long userId = UserContext.getUser();
        // 2.拼接key
        LocalDate now = LocalDate.now(); // 获取年月
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));// 这个可以得到 冒号年月
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId + format;
        // 3.利用redis bitfield来取本月第一天到今天的所有签到记录
        int dayOfMonth = now.getDayOfMonth();
        List<Long> bitField = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(bitField)){
            return new Byte[0];
        }
        Long num = bitField.get(0);
        int offset = now.getDayOfMonth() - 1;
        // 4.利用与运算  和位移 封装结果
        Byte[] arr = new Byte[dayOfMonth];

        while (offset >= 0){
            arr[offset] = (byte)(num & 1);
            offset--;
            num = num >>> 1; // 右移
        }
        return arr;
    }
}
